using System;
using System.Diagnostics;
using System.Linq.Expressions;

namespace FIFP.Common
{
    /// <summary>
    /// 
    /// </summary>
    public static class ArgumentHelper
    {
        /// <summary>
        /// Asserts that a give integer value does not meet fulfil the comparison criteria in the Func delegate.
        /// If it does, it will raise an ArgumentException.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="comparer">The comparer.</param>
        /// <param name="argName">Name of the arg.</param>
        /// <param name="message">The message.</param>
        [DebuggerHidden]
        public static void AssertIntValue(int value, Func<int, bool> comparer, string argName, string message) {
            
            if(comparer(value))
                throw new ArgumentException(message, argName);
        }

        /// <summary>
        /// Asserts that the given type is an Enum
        /// </summary>
        /// <param name="type">The type.</param>
        /// <exception cref="ArgumentException" />
        [DebuggerHidden]
        public static void AssertEnum(Type type) {

            if(!type.IsEnum)
                throw new ArgumentException("Type is not an Enumeration");
        }

        /// <summary>
        /// Asserts that the given type is an Enum
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <exception cref="ArgumentException"/>
        [DebuggerHidden]
        public static void AssertArgument(Expression<Func<bool>> expression)
        {
            var binaryExpression = expression.Body as BinaryExpression;

            var member = binaryExpression.Left as MemberExpression ?? binaryExpression.Right as MemberExpression;

            var success = expression.Compile().Invoke();

            if(!success)
                throw new ArgumentException("Invalid argmument", member.Member.Name);
        }

        /// <summary>
        /// Asserts that the <see cref="Nullable{T}"/> argument <typeparamref name="T"/> is not null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="arg">The arg.</param>
        /// <param name="argName">Name of the arg.</param>
        /// <Remarks>Created By: Steven Solomon (ssolomon) On: 05/11/2008 : 11:08 </Remarks>
        /// <exception cref="ArgumentNullException">Argument is null.</exception>
        [DebuggerHidden]
        public static void AssertNotNull<T>(T? arg, string argName) where T : struct
        {            
            if (!arg.HasValue)
            {
                throw new ArgumentNullException(argName);
            }
        }

        /// <summary>
        /// Asserts that the argument <typeparamref name="T"/> is not null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="arg">The arg.</param>
        /// <param name="argName">Name of the arg.</param>
        /// <exception cref="ArgumentNullException"><c>arg</c> is null.</exception>
        [DebuggerHidden]
        public static void AssertNotNull<T>(T arg, string argName) where T : class
        {            
            if (arg == null)
                throw new ArgumentNullException(argName);
        }

        /// <summary>
        /// Asserts that the argument <typeparamref name="T"/> is not null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="arg">The arg.</param>
        /// <param name="argName">Name of the arg.</param>
        /// <param name="message">The message.</param>
        /// <exception cref="ArgumentNullException"><c>arg</c> is null.</exception>
        [DebuggerHidden]
        public static void AssertNotNull<T>(T arg, string argName, string message) where T : class {            
            if(arg == null) {
                throw new ArgumentNullException(argName, message);
            }
        }

        /// <summary>
        /// Asserts that the argument <typeparamref name="T"/> is not null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="arg">The arg.</param>
        /// <param name="argName">Name of the arg.</param>
        /// <param name="message">The message.</param>
        /// <param name="formatArgs">The format args.</param>
        /// <exception cref="ArgumentNullException"><c>arg</c> is null.</exception>
        [DebuggerHidden]
        public static void AssertNotNull<T>(T arg, string argName, string message, params object[] formatArgs) where T : class {            
            if(arg == null) {
                throw new ArgumentNullException(argName, String.Format(message, formatArgs));
            }
        }

        /// <summary>
        /// Determines whether the specified arg is null.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="arg">The arg.</param>
        /// <returns>
        /// 	<c>true</c> if the specified arg is null; otherwise, <c>false</c>.
        /// </returns>
        [DebuggerHidden]
        public static bool IsNull<T>(T arg) where T : class
        {
            return arg == null;            
        }

        /// <summary>
        /// Asserts that a string is not null or empty
        /// </summary>
        /// <param name="arg">The arg.</param>
        /// <param name="argName">Name of the arg.</param>
        [DebuggerHidden]
        public static void AssertNotNullOrEmpty(string arg, string argName)
        {
            if(string.IsNullOrEmpty(arg))
                throw new ArgumentException("Value can not be null or empty.", argName);
        }

        /// <summary>
        /// Asserts that a string is not null or empty
        /// </summary>
        /// <param name="arg">The arg.</param>
        /// <param name="argName">Name of the arg.</param>
        /// <param name="message"></param>
        [DebuggerHidden]
        public static void AssertNotNullOrEmpty(string arg, string argName, string message) {
           if (string.IsNullOrEmpty(arg))
               throw new ArgumentException(message, argName);
        }
    }
}